
using System;
/* PDT    - marks correlated to the REPORT file showing bug fixes and improvements */

namespace Taste {

public class Parser {
	const int maxT = 29;

	const bool T = true;
	const bool x = false;
	const int minErrDist = 2;

	static Token token;      // last recognized token
	static Token t;          // lookahead token
	static int errDist = minErrDist;

	

	public static void Error(int n) {                                            /* PDT 43 */
		if (errDist >= minErrDist) Errors.SynErr(n, t.line, t.col);
		errDist = 0;
	}

	public static void SemError(int n) {                                         /* PDT 43 */
		if (errDist >= minErrDist) Errors.SemErr(n, token.line, token.col);
		errDist = 0;
	}

	public static bool Successful() {                                            /* PDT 43 */
		return Errors.count == 0;
	}

	public static string LexString() {                                           /* PDT 43 */
		return token.str;
	}

	public static string LexName() {                                             /* PDT 43 */
		return token.val;
	}

	public static string LookAheadString() {                                     /* PDT 43 */
		return t.str;
	}

	public static string LookAheadName() {                                       /* PDT 43 */
		return t.val;
	}

	static void Get() {
		for (;;) {
			token = t;
			t = Scanner.Scan();
			if (t.kind <= maxT) {errDist++; return;}                                 /* PDT 11 */

			t = token;
		}
	}

	static void Expect(int n) {
		if (t.kind == n) Get(); else Error(n);                                     /* PDT 11 */
	}

	static bool StartOf(int s) {
		return set[s, t.kind];
	}

	static void ExpectWeak(int n, int follow) {
		if (t.kind == n) Get();
		else {
			Error(n);
			while (!StartOf(follow)) Get();
		}
	}

	static bool WeakSeparator(int n, int syFol, int repFol) {
		bool[] s = new bool[maxT+1];
		if (t.kind == n) {Get(); return true;}
		else if (StartOf(repFol)) return false;
		else {
			for (int i=0; i <= maxT; i++) {
				s[i] = set[syFol, i] || set[repFol, i] || set[0, i];
			}
			Error(n);
			while (!s[t.kind]) Get();
			return StartOf(syFol);
		}
	}

	static void Taste() {
		string name, progName;
		Expect(3);
		TC.Init(); TL.Init();
		Ident(out progName);
		Expect(4);
		TC.progStart = TC.pc;
		Body();
		Ident(out name);
		if (name != progName) SemError(3);
		TC.Emit(TC.HALTc);
		Expect(5);
	}

	static void Ident(out string name) {
		Expect(1);
		name = token.val;
	}

	static void Body() {
		int fix; string name, name1; Obj obj;
		TL.EnterScope(); fix = TC.pc + 1; TC.Emit2(TC.JMP, 0);
		while (t.kind == 6 || t.kind == 8) {
			if (t.kind == 6) {
				Get();
				while (t.kind == 1) {
					Ident(out name);
					Expect(7);
					obj = TL.NewObj(name, TL.vars);
					TypeId(out obj.type);
					Expect(4);
				}
			} else {
				Get();
				Ident(out name);
				Expect(4);
				obj = TL.NewObj(name, TL.procs); obj.adr = TC.pc;
				Body();
				Ident(out name1);
				TC.Emit(TC.RET);
				if (name != name1) SemError(3);
				Expect(4);
			}
		}
		Expect(9);
		TC.Fixup(fix); TC.Emit2(TC.RES, TL.DataSpace());
		StatSeq();
		Expect(10);
		TL.LeaveScope();
	}

	static void TypeId(out int type) {
		type = TL.undef;
		if (t.kind == 11) {
			Get();
			type = TL.integer;
		} else if (t.kind == 12) {
			Get();
			type = TL.boolean;
		} else Error(30);
	}

	static void StatSeq() {
		Stat();
		while (t.kind == 4) {
			Get();
			Stat();
		}
	}

	static void Stat() {
		int type;
		string name;
		Obj obj;
		int fix, fix2, loopstart;
		if (StartOf(1)) {
			if (t.kind == 1) {
				Ident(out name);
				obj = TL.This(name);
				if (t.kind == 7) {
					Get();
					Expect(13);
					if (obj.kind != TL.vars) SemError(7);
					Expression(out type);
					if (type != obj.type) SemError(5);
					TC.Emit3(TC.STO, TL.curLevel-obj.level, obj.adr);
				} else if (t.kind == 4 || t.kind == 10 || t.kind == 16) {
					if (obj.kind != TL.procs) SemError(8);
					TC.Emit3(TC.CALL, TL.curLevel-obj.level, obj.adr);
				} else Error(31);
			} else if (t.kind == 14) {
				Get();
				Expression(out type);
				if (type != TL.boolean) SemError(6);
				fix = TC.pc + 1; TC.Emit2(TC.FJMP, 0);
				Expect(15);
				StatSeq();
				if (t.kind == 16) {
					Get();
					fix2 = TC.pc + 1; TC.Emit2(TC.JMP, 0);
					TC.Fixup(fix); fix = fix2;
					StatSeq();
				}
				Expect(10);
				TC.Fixup(fix);
			} else if (t.kind == 17) {
				Get();
				loopstart = TC.pc;
				Expression(out type);
				if (type != TL.boolean) SemError(6);
				fix = TC.pc + 1; TC.Emit2(TC.FJMP, 0);
				Expect(18);
				StatSeq();
				TC.Emit2(TC.JMP, loopstart); TC.Fixup(fix);
				Expect(10);
			} else if (t.kind == 19) {
				Get();
				Ident(out name);
				obj = TL.This(name);
				if (obj.type != TL.integer) SemError(4);
				TC.Emit3(TC.READ, TL.curLevel-obj.level, obj.adr);
			} else {
				Get();
				Expression(out type);
				if (type != TL.integer) SemError(4);
				TC.Emit(TC.WRITE);
			}
		}
	}

	static void Expression(out int type) {
		int type1, op;
		SimExpr(out type);
		if (t.kind == 13 || t.kind == 27 || t.kind == 28) {
			RelOp(out op);
			SimExpr(out type1);
			if (type != type1) SemError(5);
			TC.Emit(op); type = TL.boolean;
		}
	}

	static void SimExpr(out int type) {
		int type1, op;
		Term(out type);
		while (t.kind == 23 || t.kind == 26) {
			AddOp(out op);
			Term(out type1);
			if (type != TL.integer || type1 != TL.integer) SemError(4);
			TC.Emit(op);
		}
	}

	static void RelOp(out int op) {
		op = -1;
		if (t.kind == 13) {
			Get();
			op = TC.EQU;
		} else if (t.kind == 27) {
			Get();
			op = TC.LSS;
		} else if (t.kind == 28) {
			Get();
			op = TC.GTR;
		} else Error(32);
	}

	static void Term(out int type) {
		int type1, op;
		Factor(out type);
		while (t.kind == 24 || t.kind == 25) {
			MulOp(out op);
			Factor(out type1);
			if (type != TL.integer || type1 != TL.integer) SemError(4);
			TC.Emit(op);
		}
	}

	static void AddOp(out int op) {
		op = -1;
		if (t.kind == 26) {
			Get();
			op = TC.ADD;
		} else if (t.kind == 23) {
			Get();
			op = TC.SUB;
		} else Error(33);
	}

	static void Factor(out int type) {
		int n; Obj obj; string name;
		type = TL.undef;
		if (t.kind == 1) {
			Ident(out name);
			obj = TL.This(name); type = obj.type;
			if (obj.kind == TL.vars)
			  TC.Emit3(TC.LOAD, TL.curLevel-obj.level, obj.adr);
			else SemError(7);
		} else if (t.kind == 21) {
			Get();
			TC.Emit2(TC.LIT, 1); type = TL.boolean;
		} else if (t.kind == 22) {
			Get();
			TC.Emit2(TC.LIT, 0); type = TL.boolean;
		} else if (t.kind == 2) {
			Get();
			n = Convert.ToInt32(token.val);
			TC.Emit2(TC.LIT, n); type = TL.integer;
		} else if (t.kind == 23) {
			Get();
			Factor(out type);
			if (type != TL.integer) {SemError(4); type = TL.integer;}
			TC.Emit(TC.NEG);
		} else Error(34);
	}

	static void MulOp(out int op) {
		op = -1;
		if (t.kind == 24) {
			Get();
			op = TC.MUL;
		} else if (t.kind == 25) {
			Get();
			op = TC.DIVI;
		} else Error(35);
	}



	public static void Parse() {
		Errors.SynErr = new ErrorProc(SynErr);
		t = new Token();
		Get();
		Taste();

	}

	static void SynErr(int n, int line, int col) {
		Errors.count++;
		string s;
		switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "ident expected"; break;
			case 2: s = "number expected"; break;
			case 3: s = "PROGRAM expected"; break;
			case 4: s = "; expected"; break;
			case 5: s = ". expected"; break;
			case 6: s = "VAR expected"; break;
			case 7: s = ": expected"; break;
			case 8: s = "PROCEDURE expected"; break;
			case 9: s = "BEGIN expected"; break;
			case 10: s = "END expected"; break;
			case 11: s = "INTEGER expected"; break;
			case 12: s = "BOOLEAN expected"; break;
			case 13: s = "= expected"; break;
			case 14: s = "IF expected"; break;
			case 15: s = "THEN expected"; break;
			case 16: s = "ELSE expected"; break;
			case 17: s = "WHILE expected"; break;
			case 18: s = "DO expected"; break;
			case 19: s = "READ expected"; break;
			case 20: s = "WRITE expected"; break;
			case 21: s = "TRUE expected"; break;
			case 22: s = "FALSE expected"; break;
			case 23: s = "- expected"; break;
			case 24: s = "* expected"; break;
			case 25: s = "/ expected"; break;
			case 26: s = "+ expected"; break;
			case 27: s = "< expected"; break;
			case 28: s = "> expected"; break;
			case 29: s = "??? expected"; break;
			case 30: s = "invalid TypeId"; break;
			case 31: s = "invalid Stat"; break;
			case 32: s = "invalid RelOp"; break;
			case 33: s = "invalid AddOp"; break;
			case 34: s = "invalid Factor"; break;
			case 35: s = "invalid MulOp"; break;

			default: s = "error " + n; break;
		}
		Errors.StoreError(n, line, col, s);                                          /* PDT 40 */
	}

	static bool[,] set = {
	{T,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x,x, x,x,x},
	{x,T,x,x, x,x,x,x, x,x,x,x, x,x,T,x, x,T,x,T, T,x,x,x, x,x,x,x, x,x,x}

	};
} // end Parser
                                                                               /* PDT 47 */
                                                             
} // end namespace
